package com.ruoyi.outpatient.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.NumberUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.PersonConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.outpatient.common.MqConstants;
import com.ruoyi.outpatient.core.LoginPatient;
import com.ruoyi.outpatient.core.RedisBloomFilter;
import com.ruoyi.outpatient.core.SecurityUtils;
import com.ruoyi.outpatient.domain.*;
import com.ruoyi.outpatient.mapper.*;
import com.ruoyi.outpatient.service.IBlogService;
import com.ruoyi.outpatient.service.ICommentService;
import org.apache.commons.lang3.BooleanUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author 伟峰
 * @date 2022/4/20
 * @description: 分享业务处理
 */

@Service
public class BlogServiceImpl extends BaseController implements IBlogService {


    @Autowired
    private IBlogMapper blogMapper;

    @Autowired
    private ITagMapper tagMapper;

    @Autowired
    private ICommentMapper commentMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IPersonLoginMapper personMapper;

    @Autowired
    private IReplyMapper replyMapper;


    @Autowired
    private SecurityUtils securityUtils;



    @Autowired
    private ISimilarityMapper similarityMapper;



    @Autowired
    private RedisBloomFilter bloomFilter;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 通过分享id查询详情信息
     * @param blogId 分享id
     * @return 分享主题详情信息
     */
    @Override
    public AjaxResult selectBlogDetails(Long blogId) {

//        使用互斥锁的方式查询blog详情信息
//        声明两个方法来加锁和释放

        ReentrantLock reentrantLock=new ReentrantLock();


        Long loginPersonId = securityUtils.getLoginPersonId();

        // TODO: 2022/5/15 将详情页和评论进行异步编排
        // 使用互斥锁将blog的sql进行同步

        // TODO: 2022/5/15 注意 用户信息的更新和blog的一致性
//        1.先从redis中查询缓存（包括详情+用户+标签）
        Map<String, Object> cacheMap = redisCache.getCacheMap(PersonConstants.BLOG_DETAILS + blogId);

        if (StringUtils.isNull(cacheMap.get("blogId")))
        {

//            尝试进行加锁
            if (reentrantLock.tryLock()){
//                加锁成功,可以进行查询数据
                try {
                    //                        数据库查询
//        4.不存在根据id查询数据库(包括person+blog+tag的信息)
                    Blog blog = blogMapper.selectBlogDetails(blogId);
//        该blog对用的用户
                    Person person= personMapper.selectPerson(blog.getPerson().getPersonId());
                    blog.setPerson(person);


//        4.1查询数据库不存在返回 错误
                    if (StringUtils.isNull(blog.getBlogId())){
                        return AjaxResult.error();
                    }
//        4.2数据库存在，先写入redis中
                    Map<String, Object> map = BeanUtil.beanToMap(blog);//将对象转为Map存入redis
//                       将数据库图片转为数组返回
                    String[] split = blog.getBlogImage().split("\\^");
                    blog.setBlogImageArr(split);
                    blog.setBlogImage(null);

                    redisCache.setCacheMap(PersonConstants.BLOG_DETAILS+blogId,map); //这里存putall()
//        详情的有效期30天
                    redisCache.expire(PersonConstants.BLOG_DETAILS, Constants.BLOG_EXPIRATION,TimeUnit.DAYS); //设置一个月有效期
//                    全部弄好后进行返回

//                    封装所有的评论
                    List<CommentVo> commentVos = commentList(blogId);


                    //        // TODO: 2022/5/15 使用rabbitmq将这些统计写入
//       1. 基于内容推荐算法 ：统计用户在笔记下tag的分数
        for (Tag t:blog.getTags()){
            redisCache.incrementScoreZset("recommend:tagscore:"+blog.getPerson().getPersonId(),t.getTagName(),1);
        }
//        2. 使用set和来统计重复 ：当用户下线时插入到布隆过滤器中
        bloomFilter.insert("recommend:bloomFilter:"+blog.getPerson().getPersonId(),blogId);
        // TODO: 2022/5/13 临时代码（不清楚）
//        redisCache.setCacheSets("temporary:del:"+loginPersonId);

//        3.统计用户观看这个分享的（浏览量） 将（标题）作为本周热门事假 (过期时间一周)
        redisCache.setCacheHyperLogLog("hot:issue:"+blog.getBlogTheme(),blog.getPerson().getPersonId());
        // TODO: 2022/5/19 两天集体前刷新到数据库

        //4.统计用户对我所有帖子总浏览量
//        redisCache.setCacheHyperLogLog("myblog:pageview:"+blogId,loginPersonId);
        redisCache.setCacheHyperLogLog("statistics:allBlogPageView:"+loginPersonId,UUID.randomUUID());


//        将点赞收藏状态返回
                    Blog blogss = status(blog, loginPersonId, blogId);

                    HashMap<String,Object> hashMap=new HashMap<>();
                    hashMap.put("blog",blogss);
                    hashMap.put("comment",commentVos);

                    return AjaxResult.success().put("data",hashMap);
                }
                finally {

//                    当查询数据库后并缓存redis后,进行解锁
                    reentrantLock.unlock();

                }

            }
            else {  //获取锁不成功,说明被某个线程占用了,

//                就继续再从缓存中获取
                Map<String, Object> lockCacheMap = redisCache.getCacheMap(PersonConstants.BLOG_DETAILS + blogId);
//                1.如果没有说明,某个线程还没有存入redis,我们就先睡一会在来取
                if (StringUtils.isNull(lockCacheMap.get("blogId"))){

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
//                    睡一会后,继续查 然后返回
                    Map<String, Object> sleepCacheMap = redisCache.getCacheMap(PersonConstants.BLOG_DETAILS + blogId);

                    ////          3.存在 则将redis中的hash存储的数据转为Blog对象
            Blog blogs = BeanUtil.fillBeanWithMap(sleepCacheMap, new Blog(), false);
//                      将数据库图片转为数组返回
                    String[] split = blogs.getBlogImage().split("\\^");
                    blogs.setBlogImageArr(split);
                    blogs.setBlogImage(null);


                    List<CommentVo> commentVos=commentList(blogId);



                    //        // TODO: 2022/5/15 使用rabbitmq将这些统计写入
//       1. 基于内容推荐算法 ：统计用户在笔记下tag的分数
        for (Tag t:blogs.getTags()){
            redisCache.incrementScoreZset(blogs.getPerson().getPersonId(),t.getTagName(),1);
        }
//        2. 使用set和来统计重复 ：当用户下线时插入到布隆过滤器中
        bloomFilter.insert("recommend:bloomFilter:"+blogs.getPerson().getPersonId(),blogId);
        // TODO: 2022/5/13 临时代码（不清楚）
//        LoginPatient principal =(LoginPatient) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//        redisCache.setCacheSets("temporary:del:"+principal.getPerson().getPersonId());

//        3.统计用户观看这个分享的（浏览量） 将（标题）作为本周热门事假 (过期时间一周)
        redisCache.setCacheHyperLogLog("hot:issue:"+blogs.getBlogTheme(),blogs.getPerson().getPersonId());
        // TODO: 2022/5/19 两天集体前刷新到数据库
        //统计用户对我所有帖子总浏览量

        //4.统计这个笔记的浏览量
//        redisCache.setCacheHyperLogLog("myblog:pageview:"+blogId,loginPersonId);
     redisCache.setCacheHyperLogLog("statistics:allBlogPageView:"+loginPersonId,UUID.randomUUID());


                    Blog blogss = status(blogs, loginPersonId, blogId);
                    HashMap<String,Object> hashMap=new HashMap<>();
                    hashMap.put("blog",blogss);
                    hashMap.put("comment",commentVos);

                    return AjaxResult.success().put("data",hashMap);
                }



                ////          3.存在 则将redis中的hash存储的数据转为Blog对象
                Blog blogs = BeanUtil.fillBeanWithMap(lockCacheMap, new Blog(), false);
//                  将数据库图片转为数组返回
                String[] split = blogs.getBlogImage().split("\\^");
                blogs.setBlogImageArr(split);
                blogs.setBlogImage(null);

//                将所有的评论进行封装
                List<CommentVo> commentVos=commentList(blogId);


                //        // TODO: 2022/5/15 使用rabbitmq将这些统计写入
//       1. 基于内容推荐算法 ：统计用户在笔记下tag的分数
        for (Tag t:blogs.getTags()){
            redisCache.incrementScoreZset(blogs.getPerson().getPersonId(),t.getTagName(),1);
        }
//        2. 使用set和来统计重复 ：当用户下线时插入到布隆过滤器中
        bloomFilter.insert("recommend:bloomFilter:"+blogs.getPerson().getPersonId(),blogId);
        // TODO: 2022/5/13 临时代码（不清楚）
//        LoginPatient principal =(LoginPatient) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//        redisCache.setCacheSets("temporary:del:"+principal.getPerson().getPersonId());

//        3.统计用户观看这个分享的（浏览量） 将（标题）作为本周热门事假 (过期时间一周)
        redisCache.setCacheHyperLogLog("hot:issue:"+blogs.getBlogTheme(),blogs.getPerson().getPersonId());
        // TODO: 2022/5/19 两天集体前刷新到数据库

        //4.统计用户对我所有帖子总浏览量
//        redisCache.setCacheHyperLogLog("myblog:pageview:"+blogId,loginPersonId);
        redisCache.setCacheHyperLogLog("statistics:allBlogPageView:"+loginPersonId,UUID.randomUUID());


                Blog blogss = status(blogs, loginPersonId, blogId);
                HashMap<String,Object> hashMap=new HashMap<>();
                hashMap.put("blog",blogss);
                hashMap.put("comment",commentVos);

                return AjaxResult.success().put("data",hashMap);



            }
        }
//        说明缓存中有  直接返回
        else {


            //          3.存在 则将redis中的hash存储的数据转为Blog对象
            Blog blogs = BeanUtil.fillBeanWithMap(cacheMap, new Blog(), false);
//            将数据库图片转为数组返回
            String[] split = blogs.getBlogImage().split("\\^");
            blogs.setBlogImageArr(split);
            blogs.setBlogImage(null);
            //       用来封装所有评论
            List<CommentVo> commentVos=commentList(blogId);

            Blog blogss = status(blogs, loginPersonId, blogId);
            HashMap<String,Object> hashMap=new HashMap<>();
            hashMap.put("blog",blogss);
            hashMap.put("comment",commentVos);

            return AjaxResult.success().put("data",hashMap);


        }

    }





//    获取点赞收藏状态
    public Blog status(Blog blog,Long loginPersonId,Long blogId){
//       收藏状态
        Object collectStatus = redisCache.getMapFieldValue("collects:blog", loginPersonId + ":" + blogId);

        if (StringUtils.isNotNull(collectStatus) && (Integer.parseInt(collectStatus.toString()) != 0)){
            blog.setCollectIs(true);
        }else {
            blog.setCollectIs(false);
        }

//        点赞状态
        Object likesStatus = redisCache.getMapFieldValue("likes:blog", loginPersonId + ":" + blogId);
        if (StringUtils.isNotNull(likesStatus) && (Integer.parseInt(likesStatus.toString()) != 0)){
            blog.setLikesIs(true);
        }else {
            blog.setLikesIs(false);
        }


        return blog;

    }





    public List<CommentVo> commentList(Long blogId)
    {

        //       用来封装所有评论
        List<CommentVo> commentVos=new ArrayList<>();

//       1.先找出最热门的前10条评论  封装到List<CommentVo>
        List<Comment> voList=commentMapper.selectHotCommentByBlogId(blogId);
        if (voList.size()==0){return null;}

        for (Comment comment:voList){
            CommentVo commentVo=new CommentVo();
            BeanUtil.copyProperties(comment,commentVo);
            commentVos.add(commentVo);
        }

//        2.查找最新的评论  封装到List<CommentVo>
        List<Comment> newsList=commentMapper.selectNewsCommentByBlogId(blogId);
        if (newsList.size()==0){return commentVos;}
        for (Comment comment:newsList){
            CommentVo commentVo=new CommentVo();
            BeanUtil.copyProperties(comment,commentVo);
            commentVos.add(commentVo);
        }

        if (commentVos.size()==0){return commentVos;}
//        查找评论中对应的回复
        List<Reply> replyList = replyMapper.selectReplyListByCommentId(commentVos);
        if (replyList.size()==0){return commentVos;}

        for (CommentVo vo:commentVos){

//            将回复封装到CommentVo中
            List<Reply> replies=new ArrayList<>();
            for (Reply reply:replyList){
                if (vo.getCommentId().equals(reply.getCommentId())){
                    replies.add(reply);
                }
            }
            vo.setReplyList(replies);
        }

        return commentVos;
    }







    /**
     * 用户给blog点赞
     * @param blogId 用户点赞blogid
     * @return
     */
    @Override
    public AjaxResult likeBlog(Long blogId) {
//        1.获取点赞的登录用户
        Long loginPersonId = securityUtils.getLoginPersonId();
        //        统计互动数
        redisCache.setCacheHyperLogLog("statistics:interaction:"+loginPersonId,UUID.randomUUID());

        //        2.判断用户是否点赞过 (在blog中 看有没有该用户id) O(1)
        Object mapFieldValue = redisCache.getMapFieldValue("likes:blog", loginPersonId + ":" + blogId);

        // TODO: 2022/6/11
//        "likes:blog"设为永久key 只要定时同步数据库就行了,不需要删除key（需要使用AOF或RDB持久化）
//        blog点赞总数定时同步数据库,有无差也不怕


//        3. 只要hash不为空,并且不为0  说明为1即点过赞了
        if (StringUtils.isNotNull(mapFieldValue) && (Integer.parseInt(mapFieldValue.toString()) != 0) ) {

            //            blog点赞总数-1
            Long aLong = redisCache.setCacheDecrement("blog:likesPlusOne:" + blogId);
            if (StringUtils.isNotNull(aLong)){
                //            当blog点赞总数>-1
                if (aLong > -1) {
//                redisCache.setCacheMapValue("likes:blog", loginPersonId + ":" + blogId, 0);
//                直接删除hash字段,表示取消点赞
                    redisCache.delCacheMapField("likes:blog", loginPersonId + ":" + blogId);
                }

                //                最后统计下该用户所有blog点赞总数
//            redisCache.setCacheHyperLogLog("person:viewTotal:" + loginPersonId);

                Object cacheObject = redisCache.getCacheObject("blog:likesPlusOne:"+blogId);
                return AjaxResult.success("取消点赞成功").put("isLikes", false) //返回点赞状态 点赞总数
                        .put("likesSum",Long.valueOf(cacheObject.toString())==null ? 0:Long.valueOf(cacheObject.toString()));
            }else {
                //                去数据库查询blog总数
                long l = blogMapper.selectBlogLikes(blogId);
                redisCache.setCacheObject("blog:likesPlusOne:" + blogId,l);

                //            当blog点赞总数>-1
                if (l > -1) {
//                redisCache.setCacheMapValue("likes:blog", loginPersonId + ":" + blogId, 0);
//                直接删除hash字段,表示取消点赞
                    redisCache.delCacheMapField("likes:blog", loginPersonId + ":" + blogId);
                }
                //                最后统计下该用户所有blog点赞总数
//            redisCache.setCacheHyperLogLog("person:viewTotal:" + loginPersonId);
                Object cacheObject = redisCache.getCacheObject("blog:likesPlusOne:"+blogId);
                return AjaxResult.success("取消点赞成功").put("isLikes", false) //返回点赞状态 点赞总数
                        .put("likesSum",Long.valueOf(cacheObject.toString())==null ? 0:Long.valueOf(cacheObject.toString()));
            }



        }  //4. 只要hash不为空,并且不为0  说明为1即点过赞了
        else {

//            看key是否存储,不再去数据库  (不需要判断，因为rabbit几个小时刷新一次)
//            boolean exists = redisCache.exists("blog:likesPlusOne:" + blogId);

//             blog点赞总数+1
            Long aLong = redisCache.setCacheIncrement("blog:likesPlusOne:" + blogId);

            if (StringUtils.isNotNull(aLong)){
                //            将给用户点赞放到hash 表示点赞过
                if (aLong > -1) {
//                添加hash字段状态为1,表示进行点赞
                    redisCache.setCacheMapValue("likes:blog", loginPersonId + ":" + blogId, 1);
                }

//                最后统计下该用户所有blog点赞总数
//            redisCache.setCacheHyperLogLog("person:viewTotal:" + loginPersonId);
                Object cacheObject = redisCache.getCacheObject("blog:likesPlusOne:"+blogId);
                return AjaxResult.success("进行点赞成功").put("isLikes", true) //返回点赞状态 点赞总数
                        .put("likesSum",Long.valueOf(cacheObject.toString())==null ? 0:Long.valueOf(cacheObject.toString()));
            }else {
//                去数据库查询blog总数
                long l = blogMapper.selectBlogLikes(blogId);
                redisCache.setCacheObject("blog:likesPlusOne:" + blogId,l);

                //            将给用户点赞放到hash 表示点赞过
                if (l > -1) {
//                添加hash字段状态为1,表示进行点赞
                    redisCache.setCacheMapValue("likes:blog", loginPersonId + ":" + blogId, 1);
                }

//                最后统计下该用户所有blog点赞总数
//            redisCache.setCacheHyperLogLog("person:viewTotal:" + loginPersonId);
                Object cacheObject = redisCache.getCacheObject("blog:likesPlusOne:"+blogId);
                return AjaxResult.success("进行点赞成功").put("isLikes", true) //返回点赞状态 点赞总数
                        .put("likesSum",Long.valueOf(cacheObject.toString())==null ? 0:Long.valueOf(cacheObject.toString()));
            }

        }
    }



//    /**
//     * 用户给blog点赞
//     * @param blogId 用户点赞blogid
//     * @return
//     */
//    @Override
//    public AjaxResult likeBlog(Long blogId) {
////        1.获取点赞的登录用户
//        Long loginPersonId = securityUtils.getLoginPersonId();
////        2.判断用户是否点赞过 (在blog中 看有没有该用户id) O(1)
//        Boolean isMember = redisCache.isMemberSet("blog:likes:" + blogId,loginPersonId);
//
////        存储点赞关系 状态是否点赞
//        redisCache.setCacheMapValue("likes:blog",loginPersonId+":"+blogId,0);
//
//
//        Object mapFieldValue = redisCache.getMapFieldValue("likes:blog", loginPersonId + ":" + blogId);
//        if (Integer.parseInt(mapFieldValue.toString())==0)
//        {
//
//        }
//
////        3. 如果没有
//        if (BooleanUtils.isFalse(isMember)){
//
////            看key是否存储,不再去数据库
//            boolean exists = redisCache.exists("blog:likesPlusOne:" + blogId);
//
//            if (exists==true){
////                说明set中的key没过期
////                                          统计该blog点赞数
//                Long aLong = redisCache.setCacheIncrement("blog:likesPlusOne:" + blogId);
//
//                //            Boolean aBoolean=blogMapper.updateLikes(1); //todo 不要数据库统计 定时任务刷新
////            将给用户点赞放到set 表示点赞过
//                if (aLong>-1){
//                    redisCache.setCacheSets("blog:likes:" + blogId,loginPersonId);
//                }
//
////                最后统计下该用户所有点赞总数
//                redisCache.setCacheHyperLogLog("person:viewTotal:"+loginPersonId);
//                return AjaxResult.success("进行点赞成功").put("likesSum",aLong);
//            }
//
////                说明redis统计已经过期了,要去数据库拿
//            long likesSum=blogMapper.selectBlogLikes(blogId);
////            将数据库更新到redis中 且加一   设置15天后过期,然后定时任务在过期时刷新到数据库
//            redisCache.setCacheObject("blog:likes:" + blogId,likesSum+1,15,TimeUnit.DAYS);
//
//            //                最后统计下该用户所有点赞总数
//            redisCache.setCacheHyperLogLog("person:viewTotal:"+loginPersonId);
//
//            return AjaxResult.success("进行点赞成功").put("likesSum",likesSum);
//
//        }else { //说明用户点赞过
//
//
//            boolean exists = redisCache.exists("blog:likesPlusOne:" + blogId);
//            if (exists==true){  //说明set中的key没过期
//
//                //            redis点赞数-1
//                Long aLong = redisCache.setCacheDecrement("blog:likesPlusOne:" + blogId);
//
////                Boolean aBoolean=blogMapper.updateLikes(1); //todo 不要数据库统计 定时任务刷新
////            将给用户点赞放到set 表示点赞过
//                if (aLong>-1){
//                    redisCache.setRemove("blog:likes:" + blogId,loginPersonId);
//                }
//                return AjaxResult.success("取消点赞成功").put("likesSum",aLong);
//            }
//
////                说明redis统计已经过期了,要去数据库拿
//            long likesSum=blogMapper.selectBlogLikes(blogId);
////            将数据库更新到redis中且 减1     设置15天后过期,然后定时任务在过期时刷新到数据库
//            redisCache.setCacheObject("blog:likes:" + blogId,likesSum-1,15,TimeUnit.DAYS);
//
//            return AjaxResult.success("取消点赞成功").put("likesSum",likesSum);
//        }
//
//    }


    /**
     * 首页推介列表
     */
    @Override
    public List<Blog> selectBlogList() {

        Long loginPersonId = securityUtils.getLoginPersonId();


//        bloomFilter.init(2000,0.01);

//        1.先获取一批相似度
       List<Long> blogIdList= similarityMapper.selectSimilarityId(loginPersonId);

//       如果找不到相似度,说明是新用户或不活跃用户,直接在blog随机给他
       if (blogIdList.size()<=0){
//           找出随机的50个id
           List<Long> random=similarityMapper.selectBlogIdByRandom(50);
//           找出详情信息
           List<Blog> randomList= blogMapper.selectBlogList(random);

           return randomList;
       }
//        随机获取10条数据
        List<Long> random=similarityMapper.selectBlogIdByRandom(10);
       if (random.size()<10){
           List<Long> randoms=similarityMapper.selectBlogIdByRandom(10);
           random.addAll(randoms);
       }


       String key= "recommend:bloomFilter:"+loginPersonId;

        //        2.在布隆过滤器中过滤掉以前看过的id
        for (int i=0;i<blogIdList.size();i++)
        {
            if (bloomFilter.isBool(key,blogIdList.get(i)))
            {
//                并且每过滤掉一个就将 随机获取的插入
                blogIdList.remove(i);
                if (StringUtils.isNotNull(random.get(i)))
                {
                    blogIdList.add(i,random.get(i));
                }

            }
            System.out.println("哈哈哈哈哈哈："+blogIdList.get(i));
        }




//        3.将这些过滤的视频放到rabbitmq中，进行删除
//        监听推荐ID的过滤
        Map<String,List<Long>> map= new HashMap<>();
        map.put(key,blogIdList);
        rabbitTemplate.convertAndSend(MqConstants.CHAT_EXCHANGE,MqConstants.INSERT_ROUTING_KEY,map);



//        全部弄完,将笔记信息找出来
        List<Blog> blogs= blogMapper.selectBlogList(blogIdList);


        // TODO: 2022/6/11 后期设为管道批量获取
        for (int i=0;i<blogIdList.size();i++){
            Object mapFieldValue = redisCache.getMapFieldValue("likes:blog", loginPersonId + ":" + blogIdList.get(i));
            if (mapFieldValue==null){
                blogs.get(i).setLikesIs(false);
                continue;
            }
            if (Integer.parseInt(mapFieldValue.toString())==0){
                blogs.get(i).setLikesIs(false);
                continue;
            }
            blogs.get(i).setLikesIs(true);
        }


//        只截取第一张图片url用来显示
        for (int i=0;i<blogs.size();i++)
        {
            if (blogs.get(i).getBlogImage()!=null)
            {
                String[] split = blogs.get(i).getBlogImage().split("\\^");
                blogs.get(i).setBlogImage(split[0]);
            }
        }


        return blogs;
    }


    /**
     * 附近的人
     * @param longitude 经度
     * @param latitude 纬度
     * @return blogList
     */
    @Override
    public List<Blog> selectNearbyBlog(Double longitude, Double latitude) {


        //        找出以我为中心距离10公里的位置的人      10             公里
        Distance distances =new Distance(PersonConstants.SCOPE, Metrics.KILOMETERS);
//        将经度维度和距离我10公里传入
        Circle circle = new Circle(new Point(longitude, latitude), distances);

        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        args.includeDistance();
//        args.limit(10); 别的参数设置
//        redisTemplate.opsForGeo().radius(circle,12,3);//1:40分钟

//        获取10公里内的人需要的参数：将上面的参数封装
        GeoResults<RedisGeoCommands.GeoLocation<Long>> geoResults =
                redisCache.radiusGeo("nerby:city:3", circle, args);

        HashMap<Long,String> map=new HashMap<>(16);

//        将地图中每个blog对应的Id变量出来
        for (GeoResult<RedisGeoCommands.GeoLocation<Long>> s:geoResults)
        {
//            获取内容包含()
            RedisGeoCommands.GeoLocation<Long> content = s.getContent();
//            获取blogID
            Long personId = content.getName();
//            获取距离我公里数
            Distance distance = s.getDistance();
            double value = distance.getValue();
//            保留2位小数
            String bigDecimal = NumberUtil.round(value, 2).toString();
//            String bigDecimal = BigDecimal.valueOf(value).setScale(2,BigDecimal.ROUND_HALF_UP).toString();
//            获取单位（公里）
            String unit = distance.getUnit();

            String dist=bigDecimal+unit;

            //            map.put(Long.valueOf(idAndLocation[0]),idAndLocation[1]);
//            key存blogid  value存7.8公里
            map.put(personId,dist);
        }

//        通过动态sql将所有的blog找出来（这时还不包含位置）
        List<Blog> blogList = blogMapper.selectNearbyId(map);
        //        只截取第一张图片url用来显示
        for (int i=0;i<blogList.size();i++)
        {
            if (blogList.get(i).getBlogImage()!=null)
            {
                String[] split = blogList.get(i).getBlogImage().split("\\^");
                blogList.get(i).setBlogImage(split[0]);
            }
        }



//        将map中的位置全部放出查出来的blog集合中
        for (Blog b:blogList)
        {

            Long blogId= b.getBlogId(); //获取查询的id
            String distance = map.get(blogId); //从map中找出对应的距离
            Nearby nearby=new Nearby();
            nearby.setDistance(distance); // 放入blog
            b.setNearby(nearby);
        }

        return blogList;
    }

    /**
     * 对blog进行收藏 (和点赞的功能一样)
     * @param blogId
     * @return
     */
    @Override
    public AjaxResult likeCollect(Long blogId) {
        //        1.获取收藏的登录用户
        LoginPatient person =(LoginPatient) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

//        2.判断用户是否点赞过 (在blog中 看有没有该用户id)
        Boolean isMember = redisCache.isMemberSet("blog:collect:" + blogId,person.getPerson().getPersonId());

        if (BooleanUtils.isFalse(isMember)){
//            如果未点过赞 + 1
//            数据库点赞+1 TODO使用定时任务一段时间更新
            Boolean aBoolean=blogMapper.updateCollect(1);
//            redis点赞+1
            if (aBoolean){
                redisCache.setCacheSets("blog:collect:" + blogId,person.getPerson().getPersonId());
            }

        }else {
//            如果点过赞 - 1
//            数据库点赞-1
            Boolean aBoolean=blogMapper.updatecollect(-1);
//            redis中set将用户id移除
            if (aBoolean){
                redisCache.setRemove("blog:collect:" + blogId,person.getPerson().getPersonId());
            }
        }

        return AjaxResult.success();


    }

    /**
     * 个人发布笔记管理中心
     * @return
     */
    @Override
    public AjaxResult personBlogManagement() {
        Long loginPersonId = securityUtils.getLoginPersonId();


        //        通过个人id查询个人文章列表 创建时间排序
        List<Blog> blogs= blogMapper.selectMyBlogList(loginPersonId);
        for (Blog b:blogs){
            if (b.getBlogImage()!=null)
            {
                String[] split = b.getBlogImage().split("\\^");
                b.setBlogImage(split[0]);
            }

        }



////        将用户发布的所有笔记id分页找出来
//        Set<ZSetOperations.TypedTuple> set = redisCache.reverseRangeZSetWithScores("person:blogid:" + loginPersonId, 0, 3);
//
//        if (set.size()!=0)
//        {
//            //            将id放到list中在数据库中查找
//            List<Long> list=new ArrayList<>();
//            for (ZSetOperations.TypedTuple f:set){
//                list.add(Long.valueOf(f.getValue().toString()));
//            }
//
//
//            List<Blog> blogsId = blogMapper.selectBlogList(list);
//
//            for (int i=0;i<blogsId.size();i++)
//            {
//                if (blogsId.get(i).getBlogImage()!=null)
//                {
//                    String[] split = blogsId.get(i).getBlogImage().split("\\^");
//                    blogsId.get(i).setBlogImage(split[0]);
//                }
//            }
//            return AjaxResult.success().put("blogs",blogsId);
//        }
//
//
//        List<Blog> blogs = blogMapper.selectMyBlogList(loginPersonId);
//
//        for (int i=0;i<blogs.size();i++)
//        {
//            if (blogs.get(i).getBlogImage()!=null)
//            {
//                String[] split = blogs.get(i).getBlogImage().split("\\^");
//                blogs.get(i).setBlogImage(split[0]);
//            }
//        }

        return AjaxResult.success().put("blogs",blogs);
    }


    /**
     * 后台查询blog列表
     * @param blog
     * @return
     */
    @Override
    public List<Blog> selectGBlogList(Blog blog) {

        return blogMapper.selectGBlogList(blog);

    }


    /**
     *
     * 后台根据id删除BLOG列表
     * @param blogIds
     * @return
     */
    @Override
    public int deleteBlogByIds(Long[] blogIds) {
        return blogMapper.deleteBlogByIds(blogIds);

    }


    /**
     * 后台插入blog信息
     * @param blog
     * @return
     */
    @Override
    public int insertGBlog(Blog blog) {

        return blogMapper.insertGBlog(blog);
    }


    /**
     * 后台修改blog信息
     * @param blog
     * @return
     */
    @Override
    public int updateBlog(Blog blog) {
        return blogMapper.updateBlog(blog);
    }





//
//    /**
//     * 通过分享id查询详情信息
//     * @param blogId 分享id
//     * @return 分享主题详情信息
//     */
//    @Override
//    public AjaxResult selectBlogDetails(Long blogId) {
//
////        使用互斥锁的方式查询blog详情信息
////        声明两个方法来加锁和释放
//
//        Long loginPersonId = securityUtils.getLoginPersonId();
//
//        // TODO: 2022/5/15 将详情页和评论进行异步编排
//        // 使用互斥锁将blog的sql进行同步
//
//        // TODO: 2022/5/15 注意 用户信息的更新和blog的一致性
////        1.先从redis中查询缓存（包括详情+用户+标签）
//        Map<String, Object> cacheMap = redisCache.getCacheMap(PersonConstants.BLOG_DETAILS + blogId);
//
//        if (StringUtils.isNull(cacheMap.get("blogId")))
//        {
//
//
//        }else {
//
//
//
//
//
//        }
//
////        2.判断是否存在 blogid即redis的hash存储字段
//        if (StringUtils.isNotNull(cacheMap.get("blogId"))){
//
////          3.存在 则将redis中的hash存储的数据转为Blog对象
//            Blog blogs = BeanUtil.fillBeanWithMap(cacheMap, new Blog(), false);
//
//
//
//            //       用来封装所有评论
//            List<CommentVo> commentVos=new ArrayList<>();
//
////       1.先找出最热门的前10条评论  封装到List<CommentVo>
//            List<Comment> voList=commentMapper.selectHotCommentByBlogId(26L);
//            for (Comment comment:voList){
//                CommentVo commentVo=new CommentVo();
//                BeanUtil.copyProperties(comment,commentVo);
//                commentVos.add(commentVo);
//            }
//
////        2.查找最新的评论  封装到List<CommentVo>
//            List<Comment> newsList=commentMapper.selectNewsCommentByBlogId(blogId);
//            for (Comment comment:newsList){
//                CommentVo commentVo=new CommentVo();
//                BeanUtil.copyProperties(comment,commentVo);
//                commentVos.add(commentVo);
//            }
//
//
////        查找评论中对应的回复
//            List<Reply> replyList = replyMapper.selectReplyListByCommentId(commentVos);
//
//            for (CommentVo vo:commentVos){
//
////            将回复封装到CommentVo中
//                List<Reply> replies=new ArrayList<>();
//                for (Reply reply:replyList){
//                    if (vo.getCommentId().equals(reply.getCommentId())){
//                        replies.add(reply);
//                    }
//                }
//                vo.setReplyList(replies);
//            }
//
//
//
//
//
//
//            // TODO: 2022/5/15 使用rabbitmq将这些统计写入
////       1. 基于内容推荐算法 ：统计用户在笔记下tag的分数
//            for (Tag t:blogs.getTags()){
//                redisCache.incrementScoreZset(blogs.getPerson().getPersonId(),t.getTagName(),1);
//            }
////        2. 使用set和来统计重复 ：当用户下线时插入到布隆过滤器中
//            bloomFilter.insert("recommend:bloomFilter:"+blogs.getPerson().getPersonId(),blogId);
//            // TODO: 2022/5/13 临时代码（不清楚）
//            LoginPatient principal =(LoginPatient) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//            redisCache.setCacheSets("temporary:del:"+principal.getPerson().getPersonId());
//
////        3.统计用户观看这个分享的（浏览量） 将（标题）作为本周热门事假 (过期时间一周)
//            redisCache.setCacheHyperLogLog("hot:issue:"+blogs.getBlogTheme(),blogs.getPerson().getPersonId());
//            // TODO: 2022/5/19 两天集体前刷新到数据库
//            //统计用户对我所有帖子总浏览量
//
//            //4.统计这个笔记的浏览量
//            redisCache.setCacheHyperLogLog("myblog:pageview:"+blogId,loginPersonId);
//
//
//
//
//
//            HashMap<String,Object> hashMap=new HashMap<>();
//            hashMap.put("blog",blogs);
//            hashMap.put("comment",commentVos);
//
//            return AjaxResult.success().put("data",hashMap);
//
//
//        }
//
//
//
////                        数据库查询
//
////        4.不存在根据id查询数据库(包括person+blog+tag的信息)
//        Blog blog = blogMapper.selectBlogDetails(blogId);
////        该blog对用的用户
//        Person person= personMapper.selectPerson(blog.getPerson().getPersonId());
//        blog.setPerson(person);
//
//
//
////        4.1查询数据库不存在返回 错误
//        if (StringUtils.isNull(blog.getBlogId())){
//            return AjaxResult.error();
//        }
////        4.2数据库存在，先写入redis中
//        Map<String, Object> map = BeanUtil.beanToMap(blog);//将对象转为Map存入redis
//        redisCache.setCacheMap(PersonConstants.BLOG_DETAILS+blogId,map); //这里存putall()
////        详情的有效期30天
//        redisCache.expire(PersonConstants.BLOG_DETAILS, Constants.BLOG_EXPIRATION,TimeUnit.DAYS); //设置一个月有效期
//
//
//
//
//
//
//
//
//
//
//
//
//        // TODO: 2022/5/15 使用投喂的方式 按时间戳排序 刷新永远最新
//        // 当用户新发表一条评论，要实现前台实时展示，可以将新增的评论数向首屏列表缓存中追加最新的评论信息；
//
//
////       todo redis存储首页评论+rabbitmq进行异步通知+mysql存储其他数据
////        对于评论列表，通常访问的都是第一屏的数据，也就是第一页的数据，可以将第一页的数据缓存到redis当中，有数据更新时再通过异步程序去更新；
//
//
////       用来封装所有评论
//        List<CommentVo> commentVos=new ArrayList<>();
//
////       1.先找出最热门的前10条评论  封装到List<CommentVo>
//        List<Comment> voList=commentMapper.selectHotCommentByBlogId(26L);
//        for (Comment comment:voList){
//            CommentVo commentVo=new CommentVo();
//            BeanUtil.copyProperties(comment,commentVo);
//            commentVos.add(commentVo);
//        }
//
////        2.查找最新的评论  封装到List<CommentVo>
//        List<Comment> newsList=commentMapper.selectNewsCommentByBlogId(blogId);
//        for (Comment comment:newsList){
//            CommentVo commentVo=new CommentVo();
//            BeanUtil.copyProperties(comment,commentVo);
//            commentVos.add(commentVo);
//        }
//
//
////        查找评论中对应的回复
//        List<Reply> replyList = replyMapper.selectReplyListByCommentId(commentVos);
//
//        for (CommentVo vo:commentVos){
//
////            将回复封装到CommentVo中
//            List<Reply> replies=new ArrayList<>();
//            for (Reply reply:replyList){
//                if (vo.getCommentId().equals(reply.getCommentId())){
//                    replies.add(reply);
//                }
//            }
//            vo.setReplyList(replies);
//        }
//
//
//
//
//
//
//
//        // TODO: 2022/5/15 使用rabbitmq将这些统计写入
////       1. 基于内容推荐算法 ：统计用户在笔记下tag的分数
//        for (Tag t:blog.getTags()){
//            redisCache.incrementScoreZset(blog.getPerson().getPersonId(),t.getTagName(),1);
//        }
////        2. 使用set和来统计重复 ：当用户下线时插入到布隆过滤器中
//        bloomFilter.insert("recommend:bloomFilter:"+blog.getPerson().getPersonId(),blogId);
//        // TODO: 2022/5/13 临时代码（不清楚）
//        LoginPatient principal =(LoginPatient) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//        redisCache.setCacheSets("temporary:del:"+principal.getPerson().getPersonId());
//
////        3.统计用户观看这个分享的（浏览量） 将（标题）作为本周热门事假 (过期时间一周)
//        redisCache.setCacheHyperLogLog("hot:issue:"+blog.getBlogTheme(),blog.getPerson().getPersonId());
//        // TODO: 2022/5/19 两天集体前刷新到数据库
//        //统计用户对我所有帖子总浏览量
//
//        //4.统计这个笔记的浏览量
//        redisCache.setCacheHyperLogLog("myblog:pageview:"+blogId,loginPersonId);
//
//
//        HashMap<String,Object> hashMap=new HashMap<>();
//        hashMap.put("blog",blog);
//        hashMap.put("comment",commentVos);
//
//        return AjaxResult.success().put("data",hashMap);
//    }


}
